/************** Zhihui *****************/

#include "FinalRegion.h"
#include "contest_dm.h"
#include "Route.h"

namespace ieda_contest {

// 计算每个 GGrid 单元格在各层的总供给。
std::unordered_map<ContestCoord, int, std::hash<ContestCoord>> FinalRegion::cal_2D_Supply_map() {
    std::unordered_map<ContestCoord, int, std::hash<ContestCoord>> _2D_Supply_map;

    // 提供IdbGcellgrid水平和竖直各有多少个grid，case1为54和42。
    int32_t X_number = _data_manager->get_database()->contest_get_xnum();
    int32_t Y_number = _data_manager->get_database()->contest_get_ynum();
    int Layer_number = _data_manager->get_database()->contest_get_layernum();
    Route Route_tmp(_data_manager);

    for (int x = 0; x < X_number; x++) {
        for (int y = 0; y < Y_number; y++) {
            ContestCoord gcell_coord(x, y, 0);
            _2D_Supply_map[gcell_coord] = 0;
            for (int Layer_id = 0; Layer_id < Layer_number; Layer_id++) {
                // 将2D_Supply添加到map中
                SingleGcellInfo& cell = Route_tmp.FindCellInMap(x, y, Layer_id);
                _2D_Supply_map[gcell_coord] += cell.get_cell_supply(); 
            }
        }
    }

    
    // // 打印前10条记录
    // int count = 0;
    // for (auto it = _2D_Supply_map.begin(); it != _2D_Supply_map.end() && count < 54*42; ++it, ++count) {
    //     std::cout << "Coord: (" << it->first.get_x() << ", " << it->first.get_y() << "), Supply: " << it->second << std::endl;
    // }

    return _2D_Supply_map;
}

std::unordered_map<idb::IdbInstance *, std::pair<int, int>> FinalRegion::calFinalPos() {
    int single_gcell_x_span = _data_manager->get_database()->get_single_gcell_x_span();
    int single_gcell_y_span = _data_manager->get_database()->get_single_gcell_y_span();
    std::unordered_map<idb::IdbInstance *, std::pair<int, int>> FinalPos;
    std::vector<idb::IdbInstance *> MovableCellList;

    // 这里调用的是IdbInstance的方法不是ContestInstance的，ContestInstance没有这个接口。
    for (auto Cell : _data_manager->get_idb_design()->get_instance_list()->get_instance_list()) {
    // 与其相关联的net的数量不为零。使用 at 方法根据指向 Cell 的指针获取与 Cell 相关联的线网（nets）。
        if (_data_manager->get_database()->getRelativeNetsMap().at(Cell).size()) {
            int Row = Cell->get_coordinate()->get_x();
            int Col = Cell->get_coordinate()->get_y();

            // 真实坐标转为Gcell坐标，用myContestEvaluation::getGCellCoord会出问题。
            Row = (Row > 0 ? Row - 1 : Row) / single_gcell_x_span;
            Col = (Col > 0 ? Col - 1 : Col) / single_gcell_y_span;

            std::pair<int, int> myCellCoordinate = {Row, Col};
            FinalPos.emplace(Cell, myCellCoordinate);
            MovableCellList.emplace_back(Cell); 
            // MovableCellList其实和_data_manager->get_idb_design()->get_instance_list()->get_instance_list()一样的，因为都是可移动的。
        }
    }
    // 根据移动性和最佳区域信息，移动可移动的单元（Cell）直到不能再移动为止。
    bool Moved = false;
    int count = 0;
    do {
        Moved = false;
        for (auto CellPtr : MovableCellList) {
            int RowBeginIdx = 0, RowEndIdx = 0, ColBeginIdx = 0, ColEndIdx = 0;
            std::tie(RowBeginIdx, RowEndIdx, ColBeginIdx, ColEndIdx) = OptimalRegion::getRegion(CellPtr);
            int CurRow = 0, CurCol = 0;
            CurRow = CellPtr->get_coordinate()->get_x();
            CurCol = CellPtr->get_coordinate()->get_y();

            // 真实坐标转为Gcell坐标，用myContestEvaluation::getGCellCoord会出问题。
            CurRow = (CurRow > 0 ? CurRow - 1 : CurRow) / single_gcell_x_span;
            CurCol = (CurCol > 0 ? CurCol - 1 : CurCol) / single_gcell_y_span;

            // 检查当前单元是否不在最佳区域内。
            if (CurRow < RowBeginIdx || RowEndIdx < CurRow || CurCol < ColBeginIdx || ColEndIdx < CurCol) {
                int NewRow = 0, NewCol = 0;
                // 循环调用getBestPositionOfOptimalRegion很费时间！！
                std::tie(NewRow, NewCol) = getBestPositionOfOptimalRegion(CellPtr, RowBeginIdx, RowEndIdx, ColBeginIdx, ColEndIdx);

                // std::cout << "real_cood_old: (" << CellPtr->get_coordinate()->get_x() << ", " << CellPtr->get_coordinate()->get_y() << ")" << std::endl;
                // 这里要传入真实坐标！！用ContestInstance就可以在最开始把Instance全改为网格坐标。
                CellPtr->set_coodinate(NewRow * single_gcell_x_span + 1, NewCol * single_gcell_y_span + 1, true);
                // std::cout << "real_cood_new: (" << CellPtr->get_coordinate()->get_x() << ", " << CellPtr->get_coordinate()->get_y() << ")" << std::endl;
                Moved = true;
            }
        }

        ++count;
        // if (count >= 10) {
        //     Moved = false; //测试时用，只让for运行一轮，运行一轮就几秒。
        // }

    } while (Moved);
    // 确保在重新计算最佳区域时，使用的是每个单元最初的位置信息，而不是已经移动过的位置。
    for (auto CellPtr : MovableCellList) {
        int CurRow = 0, CurCol = 0;
        CurRow = CellPtr->get_coordinate()->get_x();
        CurCol = CellPtr->get_coordinate()->get_y();

        // 真实坐标转为Gcell坐标，用myContestEvaluation::getGCellCoord会出问题。
        CurRow = (CurRow > 0 ? CurRow - 1 : CurRow) / single_gcell_x_span;
        CurCol = (CurCol > 0 ? CurCol - 1 : CurCol) / single_gcell_y_span;

        std::pair<int, int> CurPos = {CurRow, CurCol}; // CurPos为getBestPositionOfOptimalRegion计算得到的。
        auto OldPos = FinalPos.find(CellPtr);
        // std::cout << "OldPos: (" << OldPos->second.first << ", " << OldPos->second.second << ")" << std::endl; 
        CellPtr->set_coodinate(OldPos->second.first * single_gcell_x_span + 1, OldPos->second.second * single_gcell_y_span + 1, true);
        OldPos->second = CurPos;
        // std::cout << "NewPos: (" << OldPos->second.first << ", " << OldPos->second.second << ")" << std::endl; // 第一轮打印CurRow, CurCol
    }
    return FinalPos;
}

// 最佳位置是指在该位置的网格单元具有最大的供给值（CurSupply）。
std::pair<int, int> FinalRegion::getBestPositionOfOptimalRegion
(idb::IdbInstance *CellPtr, int RowBeginIdx, int RowEndIdx, int ColBeginIdx, int ColEndIdx) {
    int NewRow = 0, NewCol = 0;
    NewRow = CellPtr->get_coordinate()->get_x();
    NewCol = CellPtr->get_coordinate()->get_y();

    // 真实坐标转为Gcell坐标，用myContestEvaluation::getGCellCoord会出问题。
    int single_gcell_x_span = _data_manager->get_database()->get_single_gcell_x_span();
    int single_gcell_y_span = _data_manager->get_database()->get_single_gcell_y_span();
    NewRow = (NewRow > 0 ? NewRow - 1 : NewRow) / single_gcell_x_span;
    NewCol = (NewCol > 0 ? NewCol - 1 : NewCol) / single_gcell_y_span;

    int MaxSupply = 0;
    // R <= RowEndIdx, C <= ColEndIdx，用的<=是因为最优区域可能是个点。
    for (auto R = RowBeginIdx; R <= RowEndIdx; ++R) {
        for (auto C = ColBeginIdx; C <= ColEndIdx; ++C) {
            ContestCoord gcell_coord(R, C, 0);
            
            auto CurSupply = _2D_Supply_map.at(gcell_coord);
            if (MaxSupply < CurSupply) {
                MaxSupply = CurSupply;
                NewRow = R;
                NewCol = C;
            }
        }
    }
    return {NewRow, NewCol};
}

// 获取给定单元格 Cell 的最终区域，并对其进行调整，确保不会超出输入数据的边界。
std::tuple<int, int, int, int> FinalRegion::getRegion(idb::IdbInstance *Cell) {
    int RowBeginIdx = 0, RowEndIdx = 0;
    int ColBeginIdx = 0, ColEndIdx = 0;
    std::tie(RowBeginIdx, ColBeginIdx) = std::tie(RowEndIdx, ColEndIdx) = FinalPos.at(Cell);

    // 提供IdbGcellgrid水平和竖直各有多少个grid，case1为54和42。
    int32_t X_number = _data_manager->get_database()->contest_get_xnum();
    int32_t Y_number = _data_manager->get_database()->contest_get_ynum();

    // 注释之后std::tie(RowBeginIdx, ColBeginIdx) = std::tie(RowEndIdx, ColEndIdx)，Move.cpp的2.3 CandidatePos里面只有一个坐标。
    // if (RowBeginIdx >= 0 + 1)
    //     --RowBeginIdx;
    // if (RowEndIdx + 1 <= X_number)
    //     ++RowEndIdx;
    // if (ColBeginIdx >= 0 + 1)
    //     --ColBeginIdx;
    // if (ColEndIdx + 1 <= Y_number)
    //     ++ColEndIdx;
    
    std::tuple<int, int, int, int> tmp(RowBeginIdx, RowEndIdx, ColBeginIdx, ColEndIdx);  // ok
    return tmp;
}

} // namespace ieda_contest